<?php

declare(strict_types=1);

/**
 * @Author:     Frank
 * @contact     frank_hhm@163.com
 * @Date:       2022-11-09 16:58:45
 * @Last Modified time: 2023-02-13 16:40:46
 */

namespace app\common\services\system;


use frank\base\BaseService;
use frank\services\CacheService;
use frank\constants\StatusCode;
use frank\services\JwtAuthService;
use frank\exception\CommonException;
use Firebase\JWT\ExpiredException;
use app\common\dao\system\AdminAuthDao;

use think\facade\Env;

/**
 * admin授权service
 * Class AdminAuthService
 * @package app\common\services\system
 */
class AdminAuthService extends BaseService
{
    /**
     * 构造方法
     * AdminAuthService constructor.
     * @param AdminAuthDao $dao
     */
    public function __construct(AdminAuthDao $dao)
    {
        $this->dao = $dao;
    }

    /**
     * 获取Admin授权信息
     * @param string $token
     * @return array
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    public function parseToken(string $token): array
    {
        $cacheService = CacheService::instance();
        if (!$token || $token === 'undefined') {
            throw new CommonException(StatusCode::ERR_LOGIN);
        }
        //设置解析token
        [$id, $type] = JwtAuthService::instance()->parseToken($token);
        //检测token是否过期
        $md5Token = Env::get('redis.jwt').md5($token);
        if (!$cacheService->hasToken($md5Token) || !($cacheToken = $cacheService->getTokenBucket($md5Token))) {
            $this->authFailAfter($id, $type);
            throw new CommonException(StatusCode::ERR_LOGIN);
        }
        //是否超出有效次数
        if (isset($cacheToken['invalidNum']) && $cacheToken['invalidNum'] >= 3) {
            if (!request()->isCli()) {
                $cacheService->clearToken($md5Token);
            }
            $this->authFailAfter($id, $type);
            throw new CommonException(StatusCode::ERR_LOGIN);
        }


        //验证token
        try {
            JwtAuthService::instance()->verifyToken();
            $cacheService->setTokenBucket($md5Token, $cacheToken, $cacheToken['exp']);
        } catch (ExpiredException $e) {
            $cacheToken['invalidNum'] = isset($cacheToken['invalidNum']) ? $cacheToken['invalidNum']++ : 1;
            $cacheService->setTokenBucket($md5Token, $cacheToken, $cacheToken['exp']);
        } catch (\Throwable $e) {
            if (!request()->isCli()) {
                $cacheService->clearToken($md5Token);
            }
            $this->authFailAfter($id, $type);
            throw new CommonException(StatusCode::ERR_LOGIN);
        }

        //获取管理员信息
        $adminInfo = $this->dao->get($id);
        if (!$adminInfo || !$adminInfo->id) {
            if (!request()->isCli()) {
                $cacheService->clearToken($md5Token);
            }
            $this->authFailAfter($id, $type);
            throw new CommonException(StatusCode::ERR_LOGIN);
        }

        $adminInfo->type = $type;
        $roles = CacheService::instance()->get('system:admin:roles:'.$adminInfo->id);
        $res['roles'] = $roles;
        $res['info'] = $adminInfo->hidden(['pwd', 'is_del', 'status'])->toArray();
        return $res;
    }

    /**
     * token验证失败后事件
     */
    protected function authFailAfter($id, $type)
    {
        try {
            $postData = request()->post();
            $rule = trim(strtolower(request()->rule()->getRule()));
            $method = trim(strtolower(request()->method()));
        } catch (\Throwable $e) {
        }
    }
}